package cube.net.frame;

import java.util.HashMap;
import java.util.Map;

import cube.data.FiFo;
import cube.net.socket.SocketConnPool;
import cube.net.socket.SocketConnect;
import cube.utils.exec.BlockExecutor;

/**
 * 帧发送器，负责将封装好的帧发送出去
 * 
 * @author abiao
 */
public abstract class FrameSender {

    /**
     * 接收帧FiFo
     */
    private Map<Identity, FiFo<NetFrame>> frameBuffer =
            new HashMap<Identity, FiFo<NetFrame>>();

    /**
     * Socket连接池
     */
    private SocketConnPool conns;

    /**
     * 帧构建器
     */
    private FrameBuilder builder;

    /**
     * 发送帧
     */
    public void send(NetFrame frame) {
        SocketConnect conn = getConnect(frame);
        if (conn != null && conn.isConnected()) {
            byte[] data = builder.getBytes(builder.build(frame));
            conn.send(data);
            watchData(conn.getIP(), conn.getPort(), data);
        }
    }

    /**
     * 获取连接
     */
    private SocketConnect getConnect(NetFrame frame) {
        return conns.get(frame.getFrameIP(), frame.getFramePort());
    }

    /**
     * 阻塞读取接收帧
     */
    public NetFrame read(final Identity identity) {
        return new BlockExecutor<NetFrame>() {
            public NetFrame action() {
                return readFrame(identity);
            }

            public boolean condition() {
                return isReceived(identity);
            }
        }.blockExecute();
    }

    /**
     * 超时阻塞读取接收帧
     */
    public NetFrame read(final Identity identity, int timeout) {
        return new BlockExecutor<NetFrame>() {
            public NetFrame action() {
                return readFrame(identity);
            }

            public boolean condition() {
                return isReceived(identity);
            }
        }.blockExecute(timeout);
    }

    /**
     * 按标识读取接收帧
     */
    private NetFrame readFrame(Identity identity) {
        FiFo<NetFrame> fifo = frameBuffer.get(identity);
        if (fifo == null) {
            return null;
        }
        return fifo.top();
    }

    /**
     * 检查指定类型帧是否已收到
     */
    private boolean isReceived(Identity identity) {
        return frameBuffer.containsKey(identity)
                && !frameBuffer.get(identity).empty();
    }

    /**
     * 将接收到的帧放入发送器的FiFo中
     */
    public void putRecvResponseFrame(NetFrame recvFrame) {
        Identity identity = recvFrame.getFrameIdentity();
        if (!frameBuffer.containsKey(identity)) {
            frameBuffer.put(identity, new FiFo<NetFrame>());
        }
        frameBuffer.get(identity).put(recvFrame);
    }

    /**
     * @return the conns
     */
    public SocketConnPool getConns() {
        return conns;
    }

    /**
     * @param conns the conns to set
     */
    public void setConns(SocketConnPool conns) {
        this.conns = conns;
    }

    /**
     * @return the builder
     */
    public FrameBuilder getBuilder() {
        return builder;
    }

    /**
     * @param builder the builder to set
     */
    public void setBuilder(FrameBuilder builder) {
        this.builder = builder;
    }

    /**
     * 查看发送出去的数据
     */
    public abstract void watchData(String ip, int port, byte[] data);
}
